perm filename DVITYP.PAS[TEX,ALS] blob
sn#596771 filedate 1981-06-24 generic text, type T, neo UTF8
(*$S2000*)
(*foo program DVITYP (dvi:-,output:+);*)
program DVITYP (dvi*,output);
(* Read, process, check and print a DVI file *)
(* fix--
tty->output in error routine?.
check for overflows before changing V and H
in general, identify sysdep features:
tfm opening, random reading,
record layouts, junks
*)
LABEL 1, (* go here for second pass *)
9; (* go here for abort *)
CONST
(* a few adjustable compile time constants *)
LOWASC=0; HIASC=127; (* for ASCII characters *)
LOWPRT=33; HIPRT=126; (* for printing ASCII characters *)
DVIID=1; (* current ID byte for DVI files *)
MAXF=63; (* maximum font number allowed *)
MAXS=200; (* maximum stack depth *)
MAXT=300; (* amount of space in which to read tfm's *)
MAXSTRLEN=100; (* max length of font names, etc *)
tab = ' ';
(* constant constants *)
ptperin=72.27; (* TEX's idea of points per inch *)
rsuperin=254000; (* RSU's per inch -- 1rsu=10↑-7 meter *)
fixperpt=1048576; (* FIX's per point -- 1pt=2↑20fix *)
(*foo*) texsys=-133207140615b; (* TEX,SYS ppn for font info *)
(* The DVI commands *)
(* VERTCHAR 0 through 127 *)
NOP=128; BOP=129; EOP=130; PST=131;
PUSH=132; POP=133;
VERTRULE=134; HORZRULE=135; HORZCHAR=136; FONT=137;
W4=138; W3=139; W2=140; W0=141;
X4=142; X3=143; X2=144; X0=145;
Y4=146; Y3=147; Y2=148; Y0=149;
Z4=150; Z3=151; Z2=152; Z0=153;
FONTNUM=154 (* through 217 *);
TYPE
(* here we disect words into bytes *)
eightbit=0..255;
sixteenbit=0..65535;
oneofthree=1..3;
hack=packed record
case oneofthree of
1:( word: integer);
2:( leftsixteen:sixteenbit;
rightsixteen:sixteenbit;
junka:0..15);
3:( byte0:eightbit;
byte1:eightbit;
byte2:eightbit;
byte3:eightbit;
junkb:0..15);
end;
(* strings of characters are done thusly: *)
ascii= LOWASC..HIASC;
str=record
len: integer;
let: packed array[1..MAXSTRLEN] of ascii;
end;
(* to hold the contents of a tfm file: *)
tfmholder=array[0..MAXT] of hack;
VAR
(*foo*) tfmfile: packed file of integer;
(*foo*) fooname: packed array[1..9] of char;
(* Variables associated with the DVI file *)
dvi: file of integer;
dviw: hack; (* current 32-bit word from dvi file *)
dvibytecnt, (* number of the byte that GETB last returned *)
dvilen, (* number of bytes in DVI file (for half pass) *)
pstptr, (* byte number of the PST command *)
dvicmd: integer;(* the current command being processed *)
foundpst: boolean;(* true when we've gotten to the PST command *)
height, width, (* for heights and widths of chars and rules *)
charno: integer;(* for character number of Horzchar cmd *)
(* constants from the postamble *)
magnify, (* overall magnification for the DVI file
(only reflected in second pass output) *)
overridemag, (* user requested over-ride for magnify *)
maxh, maxw: integer; (* maximum page height and width *)
multiplier, divider, idbyte, junkbyte: integer;
(* dvi variables *)
f, (* current font number *)
h, (* current v-coordinate *)
v, (* current h-coordinate *)
wamt, (* current w-amount *)
xamt, (* current x-amount *)
yamt, (* current y-amount *)
zamt, (* current z-amount *)
top:integer; (* top-of stack pointer *)
hstack, (* and all of the stacks *)
vstack,
wstack,
xstack,
ystack,
zstack
: array[1..MAXS] of integer;
thispageptr, (* byte number of this page's BOP command *)
lastpageptr, (* byte number of last page's BOP command *)
checkpageptr: integer; (* page pointer in BOP or PST, should=lastpageptr *)
expectbop: boolean; (* true when the next command must be BOP
(or NOP or PST) , which is right after an EOP,
and at the beginning of a DVI file. *)
eofok: boolean; (* true iff we are expecting the DVI file to end,
which is only after we have gotten to the
223-bytes at the end of the postamble *)
kount: array[0..9] of integer;
twopass, (* true iff user wants two (or 1-1/2) pass operation *)
halfpass, (* true during short (jump to postamble) first pass *)
firstpass, (* true on first (short or regular) pass *)
secondpass, (* true on second pass *)
printing, (* true when full results should be printed *)
terse: (* false to print fully on second pass, true for
just characters and their locations *)
boolean;
(* Variables associated with units, printing and converting *)
units: str; (* the name of the units we are using; one of: *)
meter,cm,pt, (* the names of the different units *)
inch,rsu: str;
convert: real; (* there are 'convert'-many 'units' in an RSU *)
dp: integer;(* number of decimal places WriteReal prints *)
(* 0 Mode control *)
(* This mode is for use to produce a condensed, quick and dirty reporting
in spite of all errors. It is initiated by replying to the pass
information request with 0. VERTCHAR commands are represented simply by
the letter involved, with normal text being typed as a continuous string of
characters, with W and X commands (or several such comands) represented by a
single space. On the appearance of a Y or Z commnd a carriage return will be
issued. This command and any additional non-VERTCHAR commands will then
be shown on the next line until the apearance of the next VERTCHAR which will
again start a new line. Any unrecognized or illegal byte appearing in the
text will be printed on a separate line.
*)
(* Printing control *)
(* In 'single pass' operation, this program will print the DVI file
from start to finish, pretty much mnemonically. Each DVI command
will be printed on a seperate line, along with its paramaters.
The program will check the validity of paramaters to the extent
possible (it checks back pointers for correctness, that EOPs are
followed by BOP's or PST, etc.). It will also print (paranthetically)
any helpfull implied paramaters that it can (it keeps track of wamt,
so it can tell you how far a W0 command should move; it even keeps
all the stacks, so it can tell you how far a Y0 should move even
after a number of PUSHes and POPs.) To get this mode, just answer
the 'how many passes?' question '1'. (If the program finds any
errors in the DVI file, it will print a diagnostic error message,
and give the user the chance to continue or abort the run.)
You will also get to answer a question about 'units'. You can
have all distances from the DVI file printed in Points, Inches,
Centimeters, Meters(?), or good old RSU's, by responding in the
indicated fashion.
If, however, the program can make a second pass over the DVI file
(read it from start to finish, and then from start to finish again),
then it can tell you lots more about the DVI file. In fact, it can
tell you exactly where each character and rule go on each page. The
reason that this requires a second pass is that there is some
critical information in the postamble, without which it is impossible
to figure out these things. But the postamble is at the end of the
DVI file, and there is no way to get to it except by reading through
the whole DVI file. What the postamble has that is so important is
the names of all the fonts that the DVI file uses. The program needs
the font names so that it can read the proper TFM files, so as to
find out the widths of all the characters. We need to know the widths
of the characters so that we know how far to move the H-COORDINATE
after a VERTCHAR command. Once we've read the postamble, and therefor
know the name of all the fonts, we can start to read the DVI file
again, this time keeping full track of exactly where the 'current
position on the page' (i.e. H- and V-COORDINATEs) is. To get this
'two pass' operation, say '2' to the 'how many passes' question.
There will be more questions to answer in this case. DVI files
have in their postambles the value of MAGNIFY that the TEX users
have requested. The value of MAGNIFY is taken into account during
the second pass, and the user of this program is given a chance to
override the value in the DVI file, to see what results that has.
This program also allows the user to specify a TERSE second pass,
in which case, it will only print out where each character and
rule is printed on each page. If TERSE is not selected, then all
the DVI commands in the file are printed, and the user gets an
informatory line before each DVI command that tells where the 'current
position on the page' is. In two pass operation, the user also
gets to specify that the entire first pass not be printed (but
the program will still check for all errors, and report any found).
There is actually a bit of a lie above that should be cleared up.
It {\sl is} actually possible to read the postamble without
reading through the entire DVI file first. But this is not a
function that is available in standard PASCAL. It is an extension
to the language that may be included in the PASCAL compiler that
you are using. If so, then by modifying a few lines below (in the
routines Dvibytecnt and Rand--maybe you can even leave them alone
if you are using the 'P20' compiler of Charles Hedrick), you can
get a kind of 'one-and-a-half' pass operation: This program
will jump to the end of the DVI file, read the postamble, then
go back to the beginning, for a normal second pass. To get this
mode of operation, say '1' to the 'Jump to postamble?' question.
*)
(* Font related variables *)
fontname: array[0..MAXF] of str; (* holds font names from postamble *)
fontmag: array[0..MAXF] of integer; (* and their magnifications *)
fontused, (* and whether they've been used
in the body of the DVI file *)
fontdefined: (* and whether they've been defined
in the postamble *)
array[0..MAXF] of boolean;
fontarea, fontext: str; (* default directory and extension
for tfm files *)
charwidth: (* widths of chars in all fonts
from their tfm files *)
array[0..MAXF,0..127] of integer;
widthloaded: (* Is charwidth[i,*] valid? *)
array[0..MAXF] of boolean; (* i.e. have we read in the character
widths for this font *)
(* temporaries *)
i: integer;
(* utility routines *)
(* returns the 'printing length' of an integer *)
function plen(i:integer):integer;
var ans: integer;
begin
if i<0 then begin i:=0-i; ans:=1; end
else ans:=0;
repeat
i:=i div 10;
ans:=ans+1;
until i=0;
plen:=ans;
end;
(* This routing prints out all possible error messages *)
(* If the error number is negative, then the error is fatal
(* otherwise, give the user the option of trying to continue.
(* Be sure to change MAXE if you add error messages
(* Don't you wish PASCAL let you pass constant strings? *)
procedure error(err,parm:integer);
CONST MAXE=28; (* error numbers are in the range 1..MAXE *)
var fatal:boolean; cont:integer;
begin
writeln(tty);
if err>0 then fatal:=false
else begin
fatal:=true;
err:=-err;
write(tty,'FATAL ');
end;
writeln(tty,'DVITYP error (number ',err:plen(err),')');
(* for the OUTPUT file *)
writeln; writeln('ERROR ',err:plen(err)); writeln;
if (0<err) and (err<=MAXE) then case err of
1: writeln(tty,'DVI eof unexpectedly at byte number ',
parm:plen(parm),'.');
2: writeln(tty,'Font number ',parm:plen(parm),
' not in range 0..',MAXF:plen(MAXF),', will use 0.');
3: writeln(tty,'Final page pointer in postamble wrong, should be ',
parm:plen(parm),'.');
4: writeln(tty,'Font ',parm:plen(parm),' used and not defined.');
5: writeln(tty,'Font ',parm:plen(parm),' defined twice.');
6: begin
writeln(tty,'TFM file larger than anticipated.');
writeln(tty,' Recompile me with MAXT > ',parm:plen(parm),'.');
end;
7: writeln(tty,'Postamble back pointer wrong, should be ',
parm:plen(parm),'.');
8: writeln(tty,'All bytes at end of postamble must be 223, ',
parm:plen(parm),' is illegal.');
9: begin
writeln(tty,'Found byte with value ',parm:plen(parm),
' while looking backwards from EOF for DVI ID.');
writeln(tty,' That''s not a 223, nor is it DVI ID (',
DVIID:plen(DVIID),').');
end;
10: writeln(tty,'DVI ID should be ',DVIID:plen(DVIID),
', not ',parm:plen(parm),'.');
11: begin
writeln(tty,'Postamble backpointer points to a byte which is ',
'not a PST command.');
writeln(tty,' The byte pointed to has value ',
parm:plen(parm),'.');
writeln(tty,' Maybe you shouldn''t try jumping to the ',
' postamble until this pointer is corrected.');
end;
12: begin
writeln(tty,'First command after EOP should be BOP or PST;');
writeln(tty,' instead found ',parm:plen(parm),'.');
end;
13: begin
writeln(tty,'VERTCHAR',parm:plen(parm),' command occured ',
' before any type of FONT command on this page.');
writeln(tty,' Will use font 0.');
end;
14: writeln(tty,'Previous page pointer should be ',
parm:plen(parm),'.');
15: writeln(tty,'Stack not empty at EOP,',
' it''s at level ',parm:plen(parm),'.');
16: writeln(tty,'Character number in HORZCHAR is bigger than 127: ',
parm:plen(parm),'; Will use 127.');
17: begin
writeln(tty,'HORZCHAR ',parm:plen(parm),' command occured ',
' before any type of FONT command on this page.');
writeln(tty,' Will use font 0.');
end;
18: begin
writeln(tty,'Your DVI Stack is bigger than expected.');
writeln(tty,' Recompile me with MAXS > ',parm:plen(parm),'.');
writeln(tty,' Ignoring this PUSH.');
end;
19: begin
writeln(tty,'More POPs than PUSHes encountered while ',
'reading a page (\count0=',parm:plen(parm),').');
writeln(tty,' Ignoring this POP');
end;
20: writeln(tty,'Undefined DVI command: ',parm:plen(parm),'.');
21: begin
writeln(tty,'First command in DVI file should be a BOP;');
writeln(tty,' instead found: ',parm:plen(parm),'.');
end;
22: writeln(tty,'Postamble Multiplier is ',parm:plen(parm),
' which isn''t > 0, will assume 1.');
23: writeln(tty,'Postamble Divider is ',parm:plen(parm),
' which isn''t > 0, will assume 1.');
24: begin
writeln(tty,'Font ',parm:plen(parm),' not defined ');
writeln(tty,' in postamble, so using 0 for all its widths');
end;
25: begin
writeln(tty,'Concatenated string would be longer than ',
MAXSTRLEN:plen(MAXSTRLEN),
' (probably while making a font file name).');
writeln(tty,' Recompile me with MAXSTRLEN > ',parm:plen(parm));
end;
26: begin
writeln(tty,'Font Name longer than ',
MAXSTRLEN:plen(MAXSTRLEN),'.');
writeln(tty,' Recompile me with MAXSTRLEN > ',parm:plen(parm));
end;
27: begin
writeln(tty,'The character whose ORD is ',parm:plen(parm),
' isn''t in the legal ascii range ',
LOWASC:plen(LOWASC),'..',HIASC:plen(HIASC),'.');
writeln(tty,' Will use ',chr(HIPRT),' instead.');
end;
28: begin
writeln(tty,'Postamble Back Pointer (',parm:plen(parm),')');
writeln(tty,' greater than number of last byte in DVI file (',
dvilen-1:plen(dvilen-1),').');
end;
end (* of case *)
else begin
writeln(tty,'Bad error number in DVITYP! ',err:plen(err));
fatal:=true;
end;
if fatal then writeln(tty,'Fatal error, quiting')
else begin
writeln(tty,'Continue? (0/1)');
break(tty);
repeat read(tty,cont) until (cont=0) or (cont=1);
end;
if fatal or (cont=0) then begin
writeln; writeln('Quiting');
goto 9;
end;
end;
(* write out an integer the right way *)
procedure writeint(int:integer);
procedure wint(int:integer);
begin
if int>0 then begin
wint(int div 10);
write((int mod 10):1);
end;
end;
begin
if int=0 then write('0')
else begin
if int<0 then begin write('-'); int:=0-int; end;
wint(int);
end;
end;
(* write out an octal number, almost the right way *)
procedure writeoct(oct:integer);
var i:integer;
procedure woct(oct:integer);
begin
if oct>0 then begin
woct(oct div 8);
write((oct mod 8):1);
end;
end;
begin
write('''');
if oct=0 then write('0')
else begin
if oct<0 then begin
write('-');
oct:=-oct; (* small bug here, of oct=-MAXINT *)
end;
woct(oct);
end;
end;
(* write out a real number in a reasonable fashion *)
procedure writereal(r:real);
var i:integer;
begin
if r<0 then begin r:=0.0-r; write('-'); end;
if r=0.0 then begin
write('0.');
for i:=1 to dp do write('0');
end
else begin
(* round it *)
r:=r+0.0009;
(* do integer part *)
i:=trunc(r);
writeint(i);
(* do fractional part *)
r:=r-i;
write('.');
for i:=1 to dp do begin
r:=r*10;
write(trunc(r):1);
r:=r-trunc(r);
end;
end;
end;
(* write out a character *)
procedure writechr(c:integer);
begin
if (LOWPRT<=c) and (c<=HIPRT) then write(chr(c))
else writeoct(c);
end;
(* write out a string *)
procedure writestr(s:str);
var i:integer;
begin
for i:=1 to s.len do writechr(s.let[i]);
end;
(* nondestructivly tack the second string onto the end of the first *)
procedure concat(var s:str; t: str);
var i,reslen: integer;
begin
reslen:=t.len+s.len;
if reslen>MAXSTRLEN then begin
error(25,reslen);
t.len:=MAXSTRLEN-s.len;
reslen:=MAXSTRLEN; end;
for i:=1 to t.len do
s.let[s.len+i]:=t.let[i];
s.len:=reslen;
end;
(* for looking at tfm files--converts FIX's to RSU's *)
function unfix(f:integer):real;
var h:hack; i:integer;
begin
h.word:=f;
if h.leftsixteen<32768 then i:= h.leftsixteen*65536+h.rightsixteen
else i:=(h.leftsixteen-65535)*65536+(h.rightsixteen-65536);
unfix:=i/fixperpt; (* converts a FIX integer to a real number of pts *)
end;
(* Gets the next byte in the DVI file, -1 if no more bytes and eofok *)
function getb:integer;
var i: integer;
begin
dvibytecnt:=dvibytecnt+1;
i:=dvibytecnt mod 4;
case i of
0:begin
if eof(dvi) then begin (* dvi↑ is invalid *)
if not eofok then error(-1,dvibytecnt);
getb:=-1;
end
else begin
dviw.word:=dvi↑;
get(dvi);
getb:=dviw.byte0;
end;
end;
1: getb:=dviw.byte1;
2: getb:=dviw.byte2;
3: getb:=dviw.byte3;
end;
end;
(* Finds out how many bytes are in the DVI file *)
function dvibytes:integer;
begin
(*foo setpos(dvi,-1); (* go to end of DVI file *)
(*foo dvibytes:=4*curpos(dvi); (* return the current postion number *)
end;
(* Does random access in DVI file such that next getb will get byte number n *)
procedure rand(n:integer);
var word,byte: integer;
begin
word:=n div 4 ; (* figure which word the requested byte is in *)
(*foo setpos(dvi,word) ; (* make it the next word to be read by wordin *)
dvibytecnt:=word*4-1 ; (* fool getb into thinking the next byte it is
to read is number word*4, so it will get a new word *)
while dvibytecnt<n-1 do byte:=getb; (* skip unwanted bytes in word *)
(* now the next getb will get byte number n *)
end;
(* Read the next 2-byte dimension in the DVI file *)
function twobytes: integer;
var n:integer;
begin
n:=getb;
if n<128 then
twobytes:=n*256+getb
else begin
twobytes:=(n-255)*256+(getb-256);
end
end;
(* Read the next 3-byte dimension in the DVI file *)
function threebytes: integer;
var n:integer;
begin
n:=getb;
if n<128 then begin
n:=n*256+getb;
threebytes:=n*256+getb;
end
else begin
n:=(n-255)*256+(getb-255);
threebytes:=n*256+(getb-256);
end
end;
(* Read the next 4-byte dimension in the DVI file *)
function fourbytes: integer;
var n:integer;
begin
n:=getb;
if n<128 then begin
n:=n*256+getb;
n:=n*256+getb;
fourbytes:=n*256+getb;
end
else begin
n:=(n-255)*256+(getb-255);
n:=n*256+(getb-255);
fourbytes:=n*256+(getb-256);
end
end;
(* Read the next 4-byte integer in the DVI file *)
function intin:integer;
begin
intin:=fourbytes;
end;
(* get next ascii char from DVI file *)
function asciiin:ascii;
var b: integer;
begin
b:=getb;
if (b<LOWASC) or (b>HIASC) then begin
error(27,b);
asciiin:=HIPRT;
end
else asciiin:=b;
end;
(* Print out a dimension *)
procedure writedimen(r:integer);
begin
writereal(convert*r);
writestr(units);
end;
(* print out the 'current position on the page' *)
procedure writeat;
begin
write('(at H='); writedimen(h);
write(', V='); writedimen(v);
write(')');
end;
(* Do a W command *)
procedure wmove(wcmd, dist: integer); begin
wamt:=dist;
if printing then begin
write('W',wcmd:1,' ');
if wcmd=0 then write('(');
writedimen(wamt);
if wcmd=0 then write(')');
end;
if secondpass then h:=h+wamt;
end;
(* Do an X command *)
procedure xmove(xcmd, dist: integer); begin
xamt:=dist;
if printing then begin
write('X',xcmd:1,' ');
if xcmd=0 then write('(');
writedimen(xamt);
if xcmd=0 then write(')');
end;
if secondpass then h:=h+xamt;
end;
(* Do a Y command *)
procedure ymove(ycmd, dist: integer); begin
yamt:=dist;
if printing then begin
write('Y',ycmd:1,' ');
if ycmd=0 then write('(');
writedimen(yamt);
if ycmd=0 then write(')');
end;
if secondpass then v:=v+yamt;
end;
(* Do a Z command *)
procedure zmove(zcmd, dist: integer); begin
zamt:=dist;
if printing then begin
write('Z',zcmd:1,' ');
if zcmd=0 then write('(');
writedimen(zamt);
if zcmd=0 then write(')');
end;
if secondpass then v:=v+zamt;
end;
(* read a font's tfm file into an array *)
(* only reads through the width info *)
procedure readtfm(fntnam:str; var tfm:tfmholder);
var
(*foo tfmfile: packed file of integer;*)
tfmname: str; (* add extension to fntnam *)
tempchr: packed array[1..MAXSTRLEN] of char;
(* for stupid RESET command *)
tfmsread: integer; (* how many words to read *)
lh,bc,ec,nw: integer; (* first parms in tfm file *)
needdirectory:boolean; (* true iff font name needs to be
augmented with a directory *)
i:integer;
begin
(* Do some SYSDEP stuff to find the name of the TFM file
associated with this font *)
tfmname.len:=0; (* make tfmname empty *)
needdirectory:=true; i:=1; (* see if font name has directory *)
while (i<=fntnam.len) and needdirectory do
begin
if (fntnam.let[i]=ord(':')) or
(fntnam.let[i]=ord('<')) then
needdirectory:=false;
i:=i+1;
end;
(* construct the tfm file name *)
if needdirectory then concat(tfmname,fontarea);
concat(tfmname,fntnam);
concat(tfmname,fontext); (* Put '.TFM' on the end *)
(* it would be nice to be able to do:
reset(tfmfile,tfmname.let)
here, but we can't because of stupid pascal.
Instead, we have to put the fntnam in a char array: *)
for i:=1 to tfmname.len do
tempchr[i]:=chr(tfmname.let[i]);
for i:=tfmname.len+1 to MAXSTRLEN do tempchr[i]:=' ';
(*foo reset(tfmfile,tempchr);*)
(*foo*)for i:=1 to 9 do fooname[i]:=' ';
(*foo*)for i:=1 to min(fntnam.len,6) do begin
(*foo*) fooname[i]:=chr(fntnam.let[i]);
(*foo*) if fntnam.let[i]>96 then fooname[i]:=chr(fntnam.let[i]-32);
(*foo*) end;
(*foo*)fooname[7]:='T'; fooname[8]:='F'; fooname[9]:='M';
(*foo*)reset(tfmfile,fooname,0,texsys);
(* Get first 6 words in TFM file *)
for i:=0 to 5 do begin
tfm[i].word:=tfmfile↑;
get(tfmfile);
end;
(* get some of the magic paramaters associated with the TFM file *)
lh:=tfm[0].rightsixteen;
bc:=tfm[1].leftsixteen;
ec:=tfm[1].rightsixteen;
nw:=tfm[2].leftsixteen;
(* compute how much of the tfm file must be read to get the entire
FINFO and WIDTH arrays, then read them in *)
tfmsread:=6+lh+(ec-bc+1)+nw;
if tfmsread>MAXT then error(-6,tfmsread);
for i:=6 to tfmsread do begin
tfm[i].word:=tfmfile↑;
get(tfmfile);
end;
end;
(* read a tfm file, filling in CHARWIDTH with the widths of the font's chars *)
procedure loadwidth(id:integer);
var
tfmname: str; (* the name of the tfm file *)
bc, ec, lh:integer; (* directly from TFM file *)
finfos: integer; (* indexαof first FINFO in TFM *)
widths: integer; (* index of first WIDTH in TFM *)
finfoloc,
widthloc: integer; (* index of this character's
FINFO and WIDTH indecies *)
tfm:tfmholder; (* holds tfm file *)
rsuperfix: real; (* font's 'AT-SIZE' in rsu's *)
i:integer;
begin
(* save the fact that we've loaded this font's widths *)
widthloaded[id]:=true;
(* if this font was never mentioned in the postamble, we can't
read in its tfm file, so don't *)
if not fontdefined[id] then begin
error(24,id);
for i:=0 to 127 do charwidth[id,i]:=0;
end
else begin
(* go ahead and read in the tfm file, and compute charwidths *)
readtfm(fontname[id],tfm);
lh:=tfm[0].rightsixteen;
bc:=tfm[1].leftsixteen;
ec:=tfm[1].rightsixteen;
finfos:=6+lh;
widths:=finfos+ec-bc+1;
(* figure out multiplier to convert from FIXes to RSUs *)
rsuperfix:=unfix(tfm[7].word)*(fontmag[id]/1000)
(* thats in points, so... *)
*(rsuperin/ptperin)
(* correct for multipli/divider *)
*(divider/multiplier);
(* writeln;
writeln('loading font ',id:plen(id));
writeln('designsize ',unfix(tfm[7].word));
writeln('fontmag ',fontmag[id]/1000);
write('rsuperfix ',rsuperfix);
writeln;
*)
(* now fill in the charwidth array *)
for i:=0 to bc-1 do charwidth[id,i]:=0;
for i:=bc to ec do begin
finfoloc:=finfos+i-bc;
widthloc:=widths+tfm[finfoloc].byte0;
charwidth[id,i]:=trunc(rsuperfix
*unfix(tfm[widthloc].word));
(*
write('charwidth[',id:plen(id),',',i:4,']=');
writedimen(charwidth[id,i]); writeln;
*)
end;
for i:=ec+1 to 127 do charwidth[id,i]:=0;
end;
end;
(* common code for FONT and FONTNUM commands *)
procedure touchfont(fnt: integer); begin
if printing then writeint(fnt);
if (fnt>MAXF) or (fnt<0) then error(2,fnt)
else f:=fnt;
(* if we'll be needing this font's charwidths, load them in *)
if secondpass and not widthloaded[f] then loadwidth(f);
if secondpass and printing then begin
write(' ('); writestr(fontname[f]);
write(' mag '); writeint(fontmag[f]); write(')'); end;
fontused[f]:=true;
end;
(* this procedure unlocks the mysteries of the postamble *)
procedure readpostamble;
var namelen, extralen, cksum, id, i
: integer; (* help read in fontdefs *)
begin
(* remember postamble byte number *)
pstptr:=dvibytecnt;
if printing then begin
write('PST (at byte '); writeint(pstptr); writeln(')'); end;
(* handle previous page pointer *)
checkpageptr:=intin;
if printing then begin
write(tab,'final page pointer: ');
writeint(checkpageptr);
writeln; end;
if (not halfpass) and (checkpageptr<>lastpageptr) then
error(3,lastpageptr);
(* handle internal unit spec *)
multiplier:=intin; divider:=intin;
if printing then begin
write(tab,'Multiplier '); writeint(multiplier);
write(tab,'Divider '); writeint(divider);
writeln; end;
if multiplier<=0 then begin error(22,multiplier); multiplier:=1; end;
if divider<=0 then begin error(23,divider); divider:=1; end;
(* handle magnification *)
if printing then write(tab,'overall magnification: ');
magnify:=intin;
if printing then begin
writeint(magnify);
if (overridemag>0) and secondpass then begin
write(' (overridden to ');
writeint(overridemag);
write(')');
end;
writeln;
end;
(* handle max height and width *)
if printing then write(tab,'maximum page height: ');
maxh:=fourbytes;
if printing then begin
writedimen(maxh); writeln;
write(tab,'maximum page width: '); end;
maxw:=fourbytes;
if printing then begin writedimen(maxw); writeln; end;
(* do all font definitions *)
id:=intin;
while id>-1 do begin (* -1 flags end of font defs *)
(* read a font definition *)
if printing then begin
write(tab,'font '); writeint(id); end;
if (id>MAXF) or (id<0) then begin error(2,id); id:=0; end;
(* font checksum *)
if printing then write(' cksum: ');
cksum:=intin;
if printing then writeoct(cksum);
(* font magnification *)
if printing then write(' mag: ');
fontmag[id]:=intin;
if printing then writeint(fontmag[id]);
(* font name *)
if printing then write(' name: ');
namelen:=getb;
if namelen>MAXSTRLEN then begin
error(26,namelen);
extralen:=namelen-MAXSTRLEN;
namelen:=MAXSTRLEN;
end
else extralen:=0;
fontname[id].len:=namelen;
for i:=1 to namelen do fontname[id].let[i]:=asciiin;
if extralen>0 then begin
write(' (dropped from font name:''');
for i:=1 to extralen do writechr(asciiin);
writeln(''')');
end;
if printing then begin
writestr(fontname[id]);
if (not fontused[id]) and (not halfpass)
then write(' (never used)');
writeln;
end;
(* check if it's multiply defined *)
if firstpass and fontdefined[id] then error(5,id);
fontdefined[id]:=true;
id:=intin; (* for next time around *)
end;
(* check to see that all fonts appearing in a FONT or FONTNUM
command actually appeared in the postamble *)
for i:=0 to MAXF do
if fontused[i] and not fontdefined[i] then error(4,i);
(* finish printing the postamble *)
(* do postamble back pointer *)
if printing then write(tab,'Postamble back pointer: ');
checkpageptr:=intin;
if printing then begin writeint(checkpageptr); writeln; end;
if checkpageptr<>pstptr then error(7,pstptr);
(* do id byte *)
if printing then write(tab,'DVI ID byte: ');
idbyte:=getb;
if printing then begin writeint(idbyte); writeln; end;
if idbyte<>DVIID then error(10,idbyte);
(* do 223 bytes after id byte *)
if printing then write('Bytes after postamble:');
eofok:=true;
junkbyte:=getb;
while junkbyte>-1 do begin
if printing then begin write(' '); writeint(junkbyte); end;
if junkbyte<>223 then error(8,junkbyte);
junkbyte:=getb; (* for next time around *)
end;
if printing then writeln;
end;
(* initialize strings, and the font arrays *)
procedure initialize;
begin
fontarea.let[1]:=ord('<'); fontarea.let[2]:=ord('T');
fontarea.let[3]:=ord('E'); fontarea.let[4]:=ord('X');
fontarea.let[5]:=ord('.'); fontarea.let[6]:=ord('F');
fontarea.let[7]:=ord('O'); fontarea.let[8]:=ord('N');
fontarea.let[9]:=ord('T'); fontarea.let[10]:=ord('S');
fontarea.let[11]:=ord('>');fontarea.len:=11;
fontext.let[1]:=ord('.'); fontext.let[2]:=ord('T');
fontext.let[3]:=ord('F'); fontext.let[4]:=ord('M'); fontext.len:=4;
meter.let[1]:=ord('m'); meter.len:=1;
cm.let[1]:=ord('c'); cm.let[2]:=ord('m'); cm.len:=2;
pt.let[1]:=ord('p'); pt.let[2]:=ord('t'); pt.len:=2;
inch.let[1]:=ord('i'); inch.let[2]:=ord('n'); inch.len:=2;
rsu.let[1]:=ord('r'); rsu.let[2]:=ord('s'); rsu.let[3]:=ord('u');
rsu.len:=3;
for i:=0 to MAXF do begin
fontused[i]:=false;
fontdefined[i]:=false;
fontname[i].len:=0;
widthloaded[i]:=false;
end;
end;
(* And here we go...main program *)
begin
initialize;
(* Set up output file *)
rewrite(output);
(* Find out in what units the user wants the results printed *)
repeat
writeln(tty,'Units? (0=RSUs, 1=meters, 2=cm, 3=inches, 4=points) ');
(*foo*) break(tty);
read(tty,i);
if (i>-1) and (i<5) then
case i of
0: begin convert:=1.0; units:=rsu; dp:=0 end;
1: begin convert:=0.0000001; units:=meter; dp:=3 end;
2: begin convert:=0.00001; units:=cm; dp:=2 end;
3: begin convert:=0.00001/2.54; units:=inch; dp:=3 end;
4: begin convert:=72.27/254000; units:=pt; dp:=2 end;
end
until (i>-1) and (i<5);
(* See if user wants two pass or half pass or one pass *)
repeat writeln(tty,'How many passes? (1/2) '); (*foo*) break(tty); read(tty,i);
until (i=1) or (i=2);
if i=2 then begin
(* two pass operation *)
twopass:=true;
(* get second pass magnification override *)
repeat
writeln(tty,'Second pass magnification? ',
'(0 for default from postamble) ');
(*foo*)break(tty);
read(tty,overridemag);
until (overridemag>=0) and (overridemag<100000);
(* see if uset wants to print the first pass *)
repeat writeln(tty,'Print first pass? (0/1) '); (*foo*)break(tty); read(tty,i);
until (i=0) or (i=1);
if i>0 then printing:=true else printing:=false;
(* see if user wants terse second pass printing *)
repeat writeln(tty,'Terse second pass? (0/1) '); (*foo*)break(tty); read(tty,i);
until (i=1) or (i=0);
if i>0 then terse:=true else terse:=false;
(* see if user wants a short first pass *)
(*foo repeat writeln(tty,'Jump to postamble? (0/1) '); read(tty,i);
until (i=0) or (i=1);
foo*)i:=0;
if i>0 then begin
(* short first pass *)
halfpass:=true; firstpass:=true; secondpass:=false;
writeln(tty,'Start quick first pass.');
(* figure out where the postamble is *)
reset(dvi);
dvilen:=dvibytes;
pstptr:=dvilen-1; (* will be backed up until correct below *)
if printing then begin
write('DVI file has '); writeint(dvilen);
write(' bytes (numbered 0 to '); writeint(pstptr);
writeln(').');
writeln('Looking at last byte in DVI file.');
end;
rand(pstptr);
junkbyte:=getb;
if junkbyte<>223 then error(9,junkbyte);
if printing then writeln('Looking backwards for DVI ID byte:');
repeat
pstptr:=pstptr-1;
if printing then writeln(tab,'Byte number ',
pstptr:plen(pstptr));
rand(pstptr);
idbyte:=getb;
if (idbyte<>223) and (idbyte<>DVIID) then
error(9,idbyte);
until idbyte=DVIID;
if printing then
writeln('Got DVI ID, now get postamble backpointer.');
pstptr:=pstptr-4;
rand(pstptr);
pstptr:=intin; (* now pstptr is finally what it claims to be *)
if printing then writeln('Postamble backpointer says that the',
' PST is in byte number ',pstptr:plen(pstptr),'.');
if pstptr>=dvilen then error(-28,pstptr);
rand(pstptr);
junkbyte:=getb;
if junkbyte <> PST then error(-11,junkbyte);
if printing then writeln('Reading Postamble.');
readpostamble;
secondpass:=true; firstpass:=false; halfpass:=false;
writeln(tty,'Finished quick first pass.');
end
else begin
(* set up for first pass of two full passes *)
halfpass:=false; firstpass:=true; secondpass:=false;
end;
end
else begin
(* just one pass *)
twopass:=false;
printing:=true; halfpass:=false; firstpass:=true; secondpass:=false;
end;
1: (* come back here to start a new pass *)
write(tty,'Starting pass ');
if firstpass then
writeln(tty,'one.');
(* I'm leaving out the ELSE here to prove that secondpass = (not firstpass) *)
if secondpass then begin
writeln(tty,'two.');
(* figure out the second pass magnification *)
if overridemag<>0 then magnify:=overridemag;
write('Second pass magnification is '); writeint(magnify); writeln;
(* and how to convert from RSUs to UNITs *)
convert:=convert*(magnify/1000)*(multiplier/divider);
if terse then printing:=false else printing:=true;
end;
(*foo*)break(tty);
reset(dvi); (* (go back and) read the DVI file from the beginning *)
top:=0; (* stack is empty *)
f:=-1; (* there is no current font *)
h:=0; v:=0; (* 'current position on page'=(0,0) *)
expectbop:=true; (* first command in DVI file must be BOP *)
dvibytecnt:=-1; (* first byte is zeroth, but GETB increments first *)
lastpageptr:=-1; (* check first page's previous page pointer *)
eofok:=false; (* not ok to find end of DVI file *)
foundpst:=false; (* keep doing this loop until foundpst=true *)
repeat
dvicmd:=getb ; (* dvicmd gets the next command in the DVI file *)
if printing then writeln;
(* skip over NOPs *)
if dvicmd=NOP then begin
repeat
if printing then write('NOP ');
dvicmd:=getb
until dvicmd<>NOP;
if printing then writeln;
end;
(* check if we are expecting a BOP *)
if expectbop and (dvicmd<>BOP) and (dvicmd<>PST) then
if dvibytecnt>0 then error(12,dvicmd)
else error(21,dvicmd);
expectbop:=false;
if printing and secondpass then begin writeat; writeln; end;
if dvicmd>217 then error(20,dvicmd)
else if dvicmd<=127 then begin
(* its a VERTCHAR *)
if printing then begin
write('VERTCHAR'); writeint(dvicmd); end;
(* print 'cpop' if needed *)
if secondpass and terse then writeat;
(* handle font number *)
if printing or (secondpass and terse) then begin
write(' (font '); writeint(f); end;
if f<0 then begin error(13,dvicmd); f:=0; end;
(* handle character name/number *)
if printing or (secondpass and terse) then begin
write(' char '); writechr(dvicmd); write(')'); end;
(* handle character's width *)
if secondpass then begin
width:=charwidth[f,dvicmd];
if printing then begin
write(' (width=');
writedimen(width);
write(')'); end;
h:=h+width;
if terse then writeln;
end;
end
else if (FONTNUM<=dvicmd) and (dvicmd<=FONTNUM+63) then begin
(* its a FONTNUM *)
if printing then write('FONTNUM');
touchfont(dvicmd-FONTNUM);
end
else case dvicmd of
BOP: begin
h:=0; v:=0; wamt:=0; xamt:=0; yamt:=0; zamt:=0;
f:=-1; (* to check for font command preceeding
any HORZ/VERT CHAR on this page *)
thispageptr:=dvibytecnt;
(* to check next page's previous page ptr *)
(* handle the count paramaters *)
if printing then begin
write('BOP (at byte '); writeint(thispageptr);
writeln(')'); write(tab); end;
for i:=0 to 4 do begin
if printing then begin
write('\count'); writeint(i); write('='); end;
kount[i]:=intin;
if printing then begin
writeint(kount[i]); write(' '); end;
end;
if printing then begin writeln; write(tab); end;
for i:=5 to 9 do begin
if printing then begin
write('\count'); writeint(i); write('='); end;
kount[i]:=intin;
if printing then begin
writeint(kount[i]); write(' '); end;
end;
(* handle previous page pointer paramater *)
if printing then begin
writeln; write(tab,'previous page at byte '); end;
checkpageptr:=intin;
if printing then writeint(checkpageptr);
if checkpageptr<>lastpageptr then error(14,lastpageptr);
end;
EOP: begin
if printing then write('EOP');
if top>0 then begin error(15,top); top:=0; end;
expectbop:=true; (* BOP or PST must be next command *)
lastpageptr:=thispageptr;
(* help check next page's prevpagepointer *)
end;
VERTRULE: begin
height:=fourbytes;
width:=fourbytes;
if printing or (secondpass and terse) then begin
if secondpass and terse then begin
writeat; write(' (rule height '); end
else write('VERTRULE height=');
writedimen(height);
write(', width='); writedimen(width);
if secondpass and terse then writeln(')');
end;
if secondpass then h:=h+width;
end;
HORZRULE: begin
height:=fourbytes;
width:=fourbytes;
if printing or (secondpass and terse) then begin
if secondpass and terse then begin
writeat; write(' (rule height '); end
else write('HORZRULE height ');
writedimen(height);
write(', width '); writedimen(width);
if secondpass and terse then writeln(')');
end;
end;
HORZCHAR: begin
if printing then write('HORZCHAR ');
charno:=getb;
if printing then writeint(charno);
if charno>127 then begin error(16,charno); charno:=127; end;
if printing or (secondpass and terse) then begin
if secondpass and terse then writeat;
write(' (font '); writeint(f);
write(' char '); writechr(charno);
write(')');
if secondpass and terse then writeln;
end;
if f<0 then begin error(17,charno); f:=0; end;
end;
FONT: begin
if printing then write('FONT ');
touchfont(intin);
end;
PUSH: begin
if printing then begin
write('PUSH (to level '); writeint(top); write(')');
end;
if top>=MAXS then error(18,MAXS)
else begin
(* stack everything up *)
top:=top+1;
wstack[top]:=wamt; xstack[top]:=xamt;
ystack[top]:=yamt; zstack[top]:=zamt;
hstack[top]:=h; vstack[top]:=v;
end;
end;
POP: begin
if printing then begin
write('POP (from level '); writeint(top); write(')');
end;
if top=0 then error(19,kount[0])
else begin
(* pop the stack *)
wamt:=wstack[top]; xamt:=xstack[top];
yamt:=ystack[top]; zamt:=zstack[top];
h:=hstack[top]; v:=vstack[top];
top:=top-1;
end;
end;
W0: wmove(0,wamt);
W2: wmove(2,twobytes);
W3: wmove(3,threebytes);
W4: wmove(4,fourbytes);
X0: xmove(0,xamt);
X2: xmove(2,twobytes);
X3: xmove(3,threebytes);
X4: xmove(4,fourbytes);
Y0: ymove(0,yamt);
Y2: ymove(2,twobytes);
Y3: ymove(3,threebytes);
Y4: ymove(4,fourbytes);
Z0: zmove(0,zamt);
Z2: zmove(2,twobytes);
Z3: zmove(3,threebytes);
Z4: zmove(4,fourbytes);
PST: begin
readpostamble;
foundpst:=true; (* to stop this loop *)
end;
end ; (* of case statement *)
until foundpst;
(*foo*)break(output);
write(tty,'Finished pass ');
if firstpass then writeln(tty,'one.');
if secondpass then writeln(tty,'two.');
(*foo*)break(tty);
(* ok, we've been through a(nother) whole pass. Should we do another? *)
if twopass and firstpass then begin
secondpass:=true; firstpass:=false;
goto 1;
end;
9: (* go here to end execution *)
end.